using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using Microsoft.VisualStudio.Modeling;
using EnvDTE80;
using DslFactory.Utilities.DteCodeModel;
using System.Net.Security;
using System.ServiceModel;

namespace Dslfactory.wcfLanguage
{

 /// <summary>
 /// Reverse engineering of message contracts
 /// </summary>
 internal static class MessageContractReverseEngineering
 {
  /// <summary>Fully qualified name of the MessageContractAttribute</summary>
  public const string messageContractAttributeFullName = "System.ServiceModel.MessageContractAttribute";

  /// <summary>Fully qualified name of the MessageBodyMemberAttribute</summary>
  public const string messageBodyMemberAttributeFullName = "System.ServiceModel.MessageBodyMemberAttribute";

  /// <summary>Fully qualified name of the MessageHeaderAttribute</summary>
  public const string messageHeaderAttributeFullName = "System.ServiceModel.MessageHeaderAttribute";

  /// <summary>
  /// Is the parameter attributeName representing a "Message contract" attribute
  /// </summary>
  public static bool IsMessageContractAttribute(string attributeName)
  {
   return IncrementalGenerator.AttributesEqual(attributeName, messageContractAttributeFullName);
  }

  /// <summary>
  /// Is the parameter attributeName representing a "Message body member" attribute
  /// </summary>
  public static bool IsMessageBodyMemberAttribute(string attributeName)
  {
   return IncrementalGenerator.AttributesEqual(attributeName, messageBodyMemberAttributeFullName);
  }

  /// <summary>
  /// Is the parameter attributeName representing a "Message header" attribute
  /// </summary>
  public static bool IsMessageHeaderAttributeFullName(string attributeName)
  {
   return IncrementalGenerator.AttributesEqual(attributeName, messageHeaderAttributeFullName);
  }

  /// <summary>
  /// Update the MessageContracts of the model
  /// </summary>
  /// <param name="servicesDefinition">Model message contract of which should be updated</param>
  /// <param name="knownCodeTypes">Known code types</param>
  public static void UpdateMessageContracts(ServicesDefinition servicesDefinition, KnownCodeTypes knownCodeTypes)
  {
   List<MessageContract> messageContracts = new List<MessageContract>(servicesDefinition.MessageContracts); // Important (collection might be modified)
   foreach (MessageContract messageContract in messageContracts)
   {
    CodeType codeType = knownCodeTypes.GetFullNamedType(messageContract.FullName);
    if (codeType != null)
     MergeMessageContract(servicesDefinition, codeType);
   }
  }

  /// <summary>
  /// Can a CodeType be considered as a Message Contract ?
  /// </summary>
  /// <param name="codeType"></param>
  /// <returns>a CodeType is considered to be a Message contract if this is a class with
  /// the MessageContractAttribute</returns>
  public static bool CanMerge(CodeType codeType)
  {
   if (codeType is CodeClass2)
    return (IncrementalGenerator.HasAttribute(codeType, messageContractAttributeFullName));
   else
    return false;
  }

  /// <summary>
  /// Update the model from the information of a CodeType considered as implenting a message contract
  /// </summary>
  /// <param name="ServicesDefinition"></param>
  /// <param name="codeType"></param>
  internal static MessageContract MergeType(ServicesDefinition ServicesDefinition, CodeType codeType)
  {
   if ((codeType is CodeClass2) && (CanMerge(codeType)))
    return MergeMessageContract(ServicesDefinition, codeType);
   else
    return null;
  }

  /// <summary>
  /// Update the model from the information of a CodeType considered as implenting a message contract
  /// </summary>
  /// <param name="ServicesDefinition"></param>
  /// <param name="codeType"></param>
  private static MessageContract MergeMessageContract(ServicesDefinition ServicesDefinition, CodeType codeType)
  {
   Store Store = ServicesDefinition.Store;
   MessageContract messageContract = ServicesDefinition.FindMessageContractOfFullName(codeType.FullName);

   // Update the data contract
   using (Transaction tUpdate = Store.TransactionManager.BeginTransaction("Updating Message contract"))
   {
    // Add a data contract
    if (messageContract == null)
    {
     messageContract = new MessageContract(Store);
     DataClassReverseEngineering.UpdateDataClassProperties(messageContract, codeType);
     ServicesDefinition.MessageContracts.Add(messageContract);
    }

    UpdateMessageContractProperties(messageContract, codeType);

    // In every case, we need to update the members (field or properties)
    CodeClass2 codeClass = codeType as CodeClass2;
    IncrementalGenerator.UpdateModelRoleFromCodeElements(messageContract.MessageMembers,
                                                         IncrementalGenerator.GetFieldsAndProperties(codeClass),
                                                         MessageMember.NameDomainPropertyId,
                                                         delegate(CodeElement e) { return e.Name; },
                                                         MessageMember.DomainClassId,
                                                         UpdateMessageMember);

    if (tUpdate.HasPendingChanges)
     tUpdate.Commit();
   }
   return messageContract;
  }


  /// <summary>
  /// Update the properties of the data contract
  /// </summary>
  /// <param name="messageContract"></param>
  /// <param name="codeType"></param>
  public static void UpdateMessageContractProperties(MessageContract messageContract, CodeType codeType)
  {
   IncrementalGenerator.UpdateDomainPropertiesFromAttributeArguments(messageContract,
                                                            IncrementalGenerator.FindFirstAttribute(codeType, messageContractAttributeFullName),
                                                            new List<string>(new string[] 
                                                                             {
                                                                              "IsWrapped",
                                                                              "ProtectionLevel",
                                                                              "WrapperName",
                                                                              "WrapperNamespace"
                                                                             })
                                                            );
  }



  /// <summary>
  /// Updates the datamember from information in the CodeElement.
  /// </summary>
  private static void UpdateMessageMember(FieldOrProperty member, CodeElement e)
  {
   // Reverse common properties
   DataClassReverseEngineering.UpdateFieldOrProperty(member, e);

   // Reverse message member specific properties (MessageMemberKind)
   MessageMember m = member as MessageMember;
   if (m != null)
    UpdateMessageMemberProperties(m, e);
  }


  /// <summary>
  /// Update 
  /// </summary>
  /// <param name="m"></param>
  /// <param name="e"></param>
  public static void UpdateMessageMemberProperties(MessageMember m, CodeElement e)
  {
   CodeAttribute2 attribute;

   // Message body
   attribute = IncrementalGenerator.FindFirstAttribute(e, messageBodyMemberAttributeFullName);
   if (attribute != null)
   {
    m.MessageMemberKind = MessageMemberKind.MessageBody;
    IncrementalGenerator.UpdateDomainPropertiesFromAttributeArguments(m,
                                                            attribute,
                                                            new List<string>(new string[] 
                                                                             {
                                                                              "Name->WCFName",
                                                                              "Namespace->WCFNamespace",
                                                                              "Order",
                                                                              "ProtectionLevel"
                                                                             })
                                                            );
    return;
   }

   // Header
   attribute = IncrementalGenerator.FindFirstAttribute(e, messageBodyMemberAttributeFullName);
   if (attribute != null)
   {
    m.MessageMemberKind = MessageMemberKind.MessageHeader;
    IncrementalGenerator.UpdateDomainPropertiesFromAttributeArguments(m,
                                                            attribute,
                                                            new List<string>(new string[] 
                                                                             {
                                                                              "Actor",
                                                                              "MustUnderstand",
                                                                              "Name->WCFName",
                                                                              "Namespace->WCFNamespace",
                                                                              "ProtectionLevel",
                                                                              "Relay",
                                                                              "EmitDefaultValue"
                                                                             })
                                                            );
    return;
   }
   else
    m.MessageMemberKind = MessageMemberKind.Normal;
  }


  internal static bool CanProcessAttributeAddedOrChanged(CodeAttribute2 attribute)
  {
   try
   {
    // If the using are not correctly set, there might be an exception
    return IsMessageContractAttribute(attribute.FullName)
       || IsMessageBodyMemberAttribute(attribute.FullName)
       || IsMessageHeaderAttributeFullName(attribute.FullName)
       ;
   }
   catch (Exception)
   {
    return IsMessageContractAttribute(attribute.Name)
       || IsMessageBodyMemberAttribute(attribute.Name)
       || IsMessageHeaderAttributeFullName(attribute.Name)
       ;
   }
  }



    internal static void ProcessAttributeAddedOrChanged(ServicesDefinition servicesDefinition, CodeAttribute2 attribute)
    {
     // Data Contact
     if (IsMessageContractAttribute(attribute.FullName))
     {
      CodeType codeClass = attribute.Parent as CodeType;
      if (codeClass != null)
      {
       MessageContract dataContact = servicesDefinition.FindMessageContractOfFullName(codeClass.FullName);
       if (dataContact == null)
        return;

       using (Transaction transaction = servicesDefinition.Store.TransactionManager.BeginTransaction(string.Format("Reversing attribute '{0}' on class '{1}'", attribute.Name, codeClass.FullName)))
       {
        UpdateMessageContractProperties(dataContact, codeClass);
        if (transaction.HasPendingChanges)
         transaction.Commit();
       }
      }
     }

     // Message member
     else if (IsMessageBodyMemberAttribute(attribute.FullName) || IsMessageHeaderAttributeFullName(attribute.FullName))
     {
      CodeElement codeElement = attribute.Parent as CodeElement;
      if (codeElement == null)
       return;
      if ((codeElement.Kind != vsCMElement.vsCMElementProperty) && (codeElement.Kind != vsCMElement.vsCMElementVariable))
       return;

      CodeType codeType = codeElement.Collection.Parent as CodeType;
      if (codeType == null)
       return;

      MessageContract messageContract = servicesDefinition.FindMessageContractOfFullName(codeType.FullName);
      if (messageContract == null)
       return;

      MessageMember messageMember = messageContract.FindMemberOfName(codeElement.Name) as MessageMember;
      if (messageMember != null)
       UpdateMessageMemberProperties(messageMember, codeElement);
     }
    }



    public static void ProcessMessageMemberAttributeRemoved(CodeElement parent, ServicesDefinition servicesDefinition)
    {
     CodeElement codeElement = parent as CodeElement;
     if (codeElement == null)
      return;
     if ((codeElement.Kind != vsCMElement.vsCMElementProperty) && (codeElement.Kind != vsCMElement.vsCMElementVariable))
      return;

     CodeType codeType = codeElement.Collection.Parent as CodeType;
     if (codeType == null)
      return;

     MessageContract messageContract = servicesDefinition.FindMessageContractOfFullName(codeType.FullName);
     if (messageContract == null)
      return;

     MessageMember dataMember = messageContract.FindMemberOfName(codeElement.Name) as MessageMember;
     if (dataMember!=null)
      dataMember.MessageMemberKind = MessageMemberKind.Normal;
     return;
    }
  
  public static void ProcessMessageContractAttributeRemoved(CodeElement parent, ServicesDefinition servicesDefinition)
  {
   CodeClass codeInterface = parent as CodeClass;
   if (codeInterface != null)
   {
    MessageContract iface = servicesDefinition.FindMessageContractOfFullName(codeInterface.FullName) as MessageContract;
    if (iface != null)
     using (Transaction transaction = servicesDefinition.Store.TransactionManager.BeginTransaction(string.Format("Removing message contract", iface.FullName)))
     {
      iface.Delete();
      transaction.Commit();
     }
   }
  }
 }
}
